iT邦幫忙

2022 iThome 鐵人賽

DAY 24
2
自我挑戰組

PixelBit 可以這樣玩!系列 第 24

(Day 24)PixelBit 俄羅斯方塊 Tetris(Part 1)

  • 分享至 

  • xImage
  •  

在之前文章中我們有用 PixelBit 玩過小恐龍,這次我們換點口味改玩俄羅斯方塊。

此次俄羅斯方塊是參考這位作者的 Code 下去重構:https://www.youtube.com/watch?v=a9YPPsduDNg

材料

下圖是我們的 PixelBit,可以看到除了 500 萬畫素鏡頭、TFT(240x240),還有左右兩顆 A、B 無段按鍵,這兩顆按鍵剛好適合我們玩各種遊戲,我們將會搭配 TFT、AB 按鍵來玩俄羅斯方塊。

Yes

程式碼說明

引用相關 Library

  • CircusUart.h 處理與 ATmega328 之間通訊
  • config.h 程式設定檔
  • tet.h 開機 logo
  • SPI.h SPI Library
  • TFT_eSPI.h TFT Library
  • TJpg_Decoder.h Jpg 圖片解碼工具
/* #region  include */
#include "CircusUart.h"
#include "config.h"
#include "tet.h"
#include <SPI.h>
#include <TFT_eSPI.h>
#include <TJpg_Decoder.h>
/* #endregion */

將積木分三個區塊

  • BlockImage 存放每種積木每一點的 Pixel 值
  • backBuffer 可放積木的區塊 buffer
  • screen 以單格積木為單位的 buffer,每顆積木占用 4 格
/* #region  buff */
uint16_t BlockImage[Block_NUM][Block_SIEZ][Block_SIEZ];     // 8 種積 Pixel,包含分隔線
uint16_t backBuffer[Height * Length][Width * Length];       // 遊戲區塊 Pixel,[Height*Length][Width*Length]
int      screen[Width][Height] = {0};                       // 存放積木區塊顏色 index (積木格數)
/* #endregion */

定義 7 種積木各個方向占用座標、可旋轉的方向數量(每個積木最多四個方向),積木顏色

/* #region  建立 7 個積木形狀、各種方向、顏色 index */
Block_t blocks[7] = {
     {{{{-1, 0}, {0, 0}, {1, 0}, {2, 0}}, {{0, -1}, {0, 0}, {0, 1}, {0, 2}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}},       2, 1}, // 長條型
     {{{{0, -1}, {1, -1}, {0, 0}, {1, 0}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}},       1, 2}, // 正方形
     {{{{-1, -1}, {-1, 0}, {0, 0}, {1, 0}}, {{-1, 1}, {0, 1}, {0, 0}, {0, -1}}, {{-1, 0}, {0, 0}, {1, 0}, {1, 1}}, {{1, -1}, {0, -1}, {0, 0}, {0, 1}}}, 4, 3}, //
     {{{{-1, 0}, {0, 0}, {0, 1}, {1, 1}}, {{0, -1}, {0, 0}, {-1, 0}, {-1, 1}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}},     2, 4},
     {{{{-1, 0}, {0, 0}, {1, 0}, {1, -1}}, {{-1, -1}, {0, -1}, {0, 0}, {0, 1}}, {{-1, 1}, {-1, 0}, {0, 0}, {1, 0}}, {{0, -1}, {0, 0}, {0, 1}, {1, 1}}}, 4, 5},
     {{{{-1, 1}, {0, 1}, {0, 0}, {1, 0}}, {{0, -1}, {0, 0}, {1, 0}, {1, 1}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}},       2, 6},
     {{{{-1, 0}, {0, 0}, {1, 0}, {0, -1}}, {{0, -1}, {0, 0}, {0, 1}, {-1, 0}}, {{-1, 0}, {0, 0}, {1, 0}, {0, 1}}, {{0, -1}, {0, 0}, {0, 1}, {1, 0}}},   4, 7}
};
/* #endregion */

宣告一些會需要用到的全域變數

/* #region  動態變數 */
Point_t pos;       // 當前積木座標
Block_t block;     // 當前積木
int     rot     = 0;
bool    started = false, gameover = false;

boolean btn_AB     = false;               // 觸發積木 旋轉
boolean btn_LEFT   = false;               // 觸發積木 往左
boolean btn_RIGHT  = false;               // 觸發積木 往右
int     game_speed = GAME_INIT_SPEED;     // 下降速度

// 紀錄按鍵狀態,避免重複觸發
// TODO: 將按鍵觸發狀態細分,並交由 328P 處理
int pom  = 0;
int pom2 = 0;
int pom3 = 0;

int score = 0;
int lvl   = 1;

ATM_BTN_STATE_E btn_b_state = ATM_BTN_REL;
ATM_BTN_STATE_E btn_a_state = ATM_BTN_REL;
/* #endregion */

建立 TFT、CircusUart object

/* #region  Object */
TFT_eSPI   tft = TFT_eSPI();
CircusUart uart(Serial);
/* #endregion */

初始化遊戲

  • 清除 screen
  • 變數初始化
  • 產生新積木
  • 繪製分數、難度等級
  • 繪製所有積木到 TFT
/* #region  初始化遊戲 */
void initGame()
{
    // 清除 screen 內容
    for (int j = 0; j < Height; ++j)
        for (int i = 0; i < Width; ++i)
            screen[i][j] = 0;
    // 變數初始化
    gameover   = false;
    score      = 0;
    game_speed = GAME_INIT_SPEED;
    lvl        = 1;
    // 產生新積木
    PutStartPos();
    /*  根據當前旋轉方向(rot)選擇 Block_t 內其中一種方向積木,
        取得 X 座標(block.square[rot][i].X)加上 X 開始座標(pos.X),
        取得 Y 座標(block.square[rot][i].Y)加上 Y 開始座標(pos.Y),
        設定積木顏色 index 到積木空間 buff(screen)內
    */
    for (int i = 0; i < 4; ++i)
        screen[pos.X + block.square[rot][i].X][pos.Y + block.square[rot][i].Y] = block.color;
    // 繪製分數、難度等級
    tft.drawString("SCORE:" + String(score), 38, 8, 1);
    tft.drawString("LVL:" + String(lvl), 167, 8, 1);

    // 繪製所有積木到 TFT
    Draw();
}
/* #endregion */

初始化遊戲積木

  • 設定下降速度
  • 積木初始座標
  • 隨機積木種類、方向
/* #region  初始化遊戲積木 */
void PutStartPos()
{
    game_speed = GAME_INIT_SPEED;
    pos.X      = 7;                           // 初始化積木 X 座標,遊戲區正中間 Width/2
    pos.Y      = 1;                           // 初始化積木 Y 座標,遊戲區最上方
    block      = blocks[random(7)];           // 隨機取積木
    rot        = random(block.numRotate);     // 隨機設定方向
}
/* #endregion */

繪製所有積木到 TFT

/* #region  更新 backBuffer,繪製 backBuffer 到 TFT */
void Draw()
{                                                    // Draw 120x240 in the center
    for (int i = 0; i < Width; ++i)                  // 水平尋訪 square
        for (int j = 0; j < Height; ++j)             // 垂直尋訪 square
            for (int k = 0; k < Length; ++k)         // 垂直尋訪 square 中 Pixel
                for (int l = 0; l < Length; ++l)     // 水平尋訪 square 中 Pixel
                                                     // 設定 backBuffer 每一點像素言顏色
                    backBuffer[j * Length + l][i * Length + k] = BlockImage[screen[i][j]][k][l];
    // 顯示 backBuffer 到 TFT
    tft.pushImage(36, 20, 165, 220, *backBuffer);
}
/* #endregion */

明天我們會繼續講剩下的方法。

更多有趣系列教學文章


上一篇
(Day 23)FPV RockBot 洛克霸
下一篇
(Day 25)PixelBit 俄羅斯方塊 Tetris(Part 2)
系列文
PixelBit 可以這樣玩!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言